
private {
	import utils.Singleton;
	import Screen;

	import std.string:toStringz;
	static import Perlin;

	import maths.Vec;
	import std.math:cos,sin;
	import std.c.stdlib:malloc,random,srand;

	import derelict.sdl.sdl;
	import derelict.opengl.gl;
	import derelict.opengl.glu;
}

// horizontal and vertical screen resolution
const int   xResolution     = 800;
const int   yResolution     = 600;
const float	aspect			= 1.333333333f;

// number of bits per pixel used for display. 24 => true color
const int   bitsPerPixel    = 24;

// field of view => the angle our camera will see vertically
const float fov             = 90.f;

// distance of the near clipping plane
const float nearPlane       = 1f;

// distance of the far clipping plane
const float farPlane        = 2500.f;

class Renderer {
	mixin SingletonMix;
	
	
	void initialize() {
	    DerelictSDL.load();
    	DerelictGL.load();
	    DerelictGLU.load();

    	// initialize SDL's VIDEO module
	    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);

    	// enable double-buffering
	    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

    	// create our OpenGL window
	    SDL_SetVideoMode(xResolution, yResolution, bitsPerPixel, SDL_OPENGL | SDL_RESIZABLE);
    	SDL_WM_SetCaption(toStringz("LGiM"), null);
//		DerelictGL.loadVersions(GLVersion.Version20);
    	SDL_ShowCursor(SDL_DISABLE);


		glEnable(GL_DEPTH_TEST);	// Hidden surface removal
		glFrontFace(GL_CCW);		// Counter clock-wise polygons face out
		glEnable(GL_CULL_FACE);		// Cull back-facing triangles

		glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

		glEnable(GL_COLOR_MATERIAL);
	
		glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

		GLfloat s_vector[4] = [ 1.0f/cast(float)textureSize, 0f, 0f, 0f ];
		GLfloat t_vector[4] = [ 0f, 0f, 1.0f/cast(float)textureSize, 0f ];

		glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
		glTexGenfv( GL_S, GL_OBJECT_PLANE, s_vector );

		glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
		glTexGenfv( GL_T, GL_OBJECT_PLANE, t_vector );

	    glViewport(0, 0, 800, 600);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
	
		{
			double  right = 1 * tan( fov/2.0 * 3.14f/180.0f );
			double  left;

			double  bottom;
			double  top;
		
		
			top = right / aspect;
			bottom = -top;
			left = -right;
			glFrustum(left, right, bottom, top, nearPlane, farPlane);
		}

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		generateTexture();
	}
	
	~this(){
	    // tell SDL to quit
    	SDL_Quit();

		// release GL, GLU and SDL's shared libs
	    DerelictGLU.unload();
    	DerelictGL.unload();
	    DerelictSDL.unload();
	}

	void set(){
		mode();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();

		glScalef(1.0f, scale, 1.0f);
	}

	void end(){
		glPopMatrix();
		SDL_GL_SwapBuffers();
	}

	void initPatch(float x, float y){
		glPushMatrix();
		glTranslatef(x, 0, y);
		glBegin(GL_TRIANGLES);
	}

	void endPatch(){
		glEnd();
		glPopMatrix();
	}

	void drawTriangle(float leftX, float leftZ, float leftY,
			float rightX, float rightZ, float rightY,
			float apexX, float apexZ, float apexY){

			float fColor = (60.0f + leftZ) / 256.0f;
			if ( fColor > 1.0f )  fColor = 1.0f;
			glColor3f( fColor, fColor, fColor );
			glVertex3f(leftX, leftZ, leftY);

			fColor = (60.0f + rightZ) / 256.0f;
			if ( fColor > 1.0f )  fColor = 1.0f;
			glColor3f( fColor, fColor, fColor );
			glVertex3f(rightX, rightZ, rightY);

			fColor = (60.0f + apexZ) / 256.0f;
			if ( fColor > 1.0f )  fColor = 1.0f;
			glColor3f( fColor, fColor, fColor );
			glVertex3f(apexX, apexZ, apexY);
	}

	void generateTexture(){
		glBindTexture(GL_TEXTURE_2D, textures[0]);
	
		ubyte *pTexture = cast(ubyte*)malloc(textureSize*textureSize*3);
		ubyte *pTexWalk = pTexture;
		double w;
		ubyte color;

		srand(100);

		for ( int x = 0; x < textureSize; x++ )
			for ( int y = 0; y < textureSize; y++ ){

				w = (Perlin.fullNoise(cast(double)x, cast(double)random(textureSize), cast(double)y, .01, 10) + 1.)/2.0;
				assert(w >= 0.f);
				assert(w <= 1.f);
//				color = cast(ubyte)(w * 256.);
				color = cast(ubyte)(128.0+(40.0 * random(10000))/10000);
				*(pTexWalk++) = 0;
				*(pTexWalk++) = color;	// Only use the Green chanel.
				*(pTexWalk++) = 0;
/+				pTexture[3*(x*textureSize + y) + 0] = 0;
				pTexture[3*(x*textureSize + y) + 1] = color;
				pTexture[3*(x*textureSize + y) + 2] = 0;

				pTexture[3*((textureSize/2-x)*textureSize + y) + 0] = 0;
				pTexture[3*((textureSize/2-x)*textureSize + y) + 1] = color;
				pTexture[3*((textureSize/2-x)*textureSize + y) + 2] = 0;+/
			}

		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, textureSize, textureSize, GL_RGB, GL_UNSIGNED_BYTE, pTexture);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);

		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	}

	void mode(){
		if(wireframe){
			modeWire();
		}
		if(tex){
			modeTex();
		}
	}

	void modeTex(){
		wireframe = false;
		tex = true;
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glPolygonMode(GL_FRONT, GL_FILL);
	}

	void modeWire(){
		wireframe = true;
		tex = false;
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glPolygonMode(GL_FRONT, GL_LINE);
	}

	private {
		uint[1] textures;
		bool wireframe = false;
		bool tex = true;
	}

	public {
		float scale = 0.5f;
		uint textureSize = 128;
	}
}


alias SingletonGetter!(Renderer) renderer;
